// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraNumberPopComponent_MeshText.h"

#include "Components/StaticMeshComponent.h"
#include "Engine/CollisionProfile.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "Feedback/NumberPops/LyraNumberPopComponent.h"
#include "LyraDamagePopStyle.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "TimerManager.h"
#include "UObject/Package.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraNumberPopComponent_MeshText)

class UStaticMesh;

/**
 * 构造函数
 */
ULyraNumberPopComponent_MeshText::ULyraNumberPopComponent_MeshText(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	ComponentLifespan = 1.f; // 组件生命周期初始化为1秒

	SignDigitParameterName = FName(TEXT("+Or-")); // 初始化符号数字参数名称
	ColorParameterName = FName(TEXT("Color")); // 初始化颜色参数名称
	AnimationLifespanParameterName = FName(TEXT("Animation Lifespan")); // 初始化动画生命周期参数名称
	IsCriticalHitParameterName = FName(TEXT("isCriticalHit?")); // 初始化是否为暴击参数名称
	MoveToCameraParameterName = FName(TEXT("MoveToCamera")); // 初始化移动到相机参数名称
	PositionParameterNames = { TEXT("0a"), TEXT("1a"), TEXT("2a"), TEXT("3a"), TEXT("4a"),  TEXT("5a"),  TEXT("6a"),  TEXT("7a"),  TEXT("8a") }; // 初始化位置参数名称数组
	ScaleRotationAngleParameterNames = { TEXT("0b"), TEXT("1b"), TEXT("2b"), TEXT("3b"), TEXT("4b"),  TEXT("5b"),  TEXT("6b"),  TEXT("7b"),  TEXT("8b") }; // 初始化缩放旋转角度参数名称数组
	DurationParameterNames = { TEXT("0c"), TEXT("1c"), TEXT("2c"), TEXT("3c"), TEXT("4c"),  TEXT("5c"),  TEXT("6c"),  TEXT("7c"),  TEXT("8c") }; // 初始化持续时间参数名称数组

	SpacingPercentageForOnes = 0.8f; // 数字1的间距百分比初始化为0.8


	DistanceFromCameraBeforeDoublingSize = 1024.f; // 距离相机多远时尺寸加倍初始化为1024
	CriticalHitSizeMultiplier = 1.7f; // 暴击尺寸乘数初始化为1.7

	FontXSize = 10.920001f; // 字体 X 尺寸初始化为10.920001
	FontYSize = 21.0f; // 字体 Y 尺寸初始化为21.0

	NumberOfNumberRotations = 1.f; // 数字旋转次数初始化为1
}

/**
 * 添加数字弹出效果
 */
void ULyraNumberPopComponent_MeshText::AddNumberPop(const FLyraNumberPopRequest& NewRequest)
{
	// Drop requests for remote players on the floor
	// (this prevents multiple pops from showing up for the host of a listen server)
	// 忽略远程玩家的请求（这可以防止在监听服务器主机上出现多个弹出效果）
	if (APlayerController* PC = GetController<APlayerController>())
	{
		if (!PC->IsLocalController())
		{
			return;
		}
	}

	FTempNumberPopInfo PreparedNumberInfo; // 准备数字信息

	// Prepare the DamageNumberArray with the digits from the damage.
	// 使用伤害的数字准备 DamageNumberArray
	{
		int32 LocalDamage = NewRequest.NumberToDisplay; // 本地伤害数字
		PreparedNumberInfo.DamageNumberArray.Empty(); // 清空伤害数字数组

		if (LocalDamage == 0)
		{
			// We want to just show a zero
			// 我们只想显示一个零
			PreparedNumberInfo.DamageNumberArray.Insert(0, 0); // 在位置0插入0
		}
		else
		{
			// Parse the base10 number into an array
			// 将十进制数字解析为数组
			while (LocalDamage > 0)
			{
				PreparedNumberInfo.DamageNumberArray.Insert(LocalDamage % 10, 0); // 在位置0插入个位数
				LocalDamage /= 10; // 除以10
			}
		}

		// Insert a zero to reserve space for + or -. Used by the blueprint
		// 插入一个零以为 + 或 - 保留空间。由蓝图使用。
		PreparedNumberInfo.DamageNumberArray.Insert(0, 0); // 在位置0插入0
	}

	// Grab a component from the pool for this number or create one
	// 从池中获取一个组件用于此数字或创建一个
	{
		UStaticMesh* MeshToUse = DetermineStaticMesh(NewRequest); // 确定要使用的静态网格
		if (MeshToUse == nullptr)
		{
			return;
		}

		FPooledNumberPopComponentList& ComponentPool = PooledComponentMap.FindOrAdd(MeshToUse); // 查找或添加组件池

		UStaticMeshComponent* ComponentToUse = nullptr; // 要使用的组件
		if (ComponentPool.Components.Num() > 0)
		{
			ComponentToUse = ComponentPool.Components.Pop(); // 从组件池中弹出一个组件
		}
		else
		{
			ComponentToUse = NewObject<UStaticMeshComponent>(GetOwner()); // 创建新的静态网格组件
			ComponentToUse->SetupAttachment(nullptr); // 设置附件为null
			ComponentToUse->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName); // 设置碰撞配置文件为无碰撞
			ComponentToUse->SetStaticMesh(MeshToUse); // 设置静态网格

			// Used to allow post-processes to opt out of affecting the number pop digits
			// 用于允许后处理选择不影响数字弹出数字
			ComponentToUse->SetRenderCustomDepth(true); // 设置渲染自定义深度为true
			ComponentToUse->SetCustomDepthStencilValue(123); // 设置自定义深度模板值为123

			// The digits travel a great distance from their original bounds due to
			// world position offset (WPO) animation in the material, so expand bounds
			// 由于材质中的世界位置偏移（WPO）动画，数字会远离其原始边界，因此扩展边界
			ComponentToUse->SetBoundsScale(2000.0f); // 设置边界缩放为2000.0

			// We'll be overriding values like the desired color and digits to use, so we need MIDs
			// 我们将覆盖诸如所需颜色和要使用的数字之类的值，因此需要材质实例动态（MIDs）
			for (int32 MatIdx = 0; MatIdx < ComponentToUse->GetNumMaterials(); ++MatIdx)
			{
				ComponentToUse->CreateDynamicMaterialInstance(MatIdx); // 创建动态材质实例
			}
		}

		// Register
		// 注册
		check(ComponentToUse);
		ComponentToUse->RegisterComponent();

		// Add to the "live" list
		// 添加到“活跃”列表
		UWorld* LocalWorld = GetWorld();
		check(LocalWorld);
		LiveComponents.Emplace(ComponentToUse, &ComponentPool, LocalWorld->GetTimeSeconds() + ComponentLifespan); // 添加到活跃组件数组

		// Assign struct pointers
		// 分配结构体指针
		PreparedNumberInfo.StaticMeshComponent = ComponentToUse; // 设置静态网格组件
		for (int32 MatIdx = 0; MatIdx < ComponentToUse->GetNumMaterials(); ++MatIdx)
		{
			UMaterialInstanceDynamic* NewMID = Cast<UMaterialInstanceDynamic>(ComponentToUse->GetMaterial(MatIdx)); // 获取材质实例动态
			PreparedNumberInfo.MeshMIDs.Add(NewMID); // 添加到网格材质实例动态数组
		}

		// Start the timer if it wasn't already running
		// 如果计时器尚未运行，则启动它
		if (!LocalWorld->GetTimerManager().IsTimerActive(ReleaseTimerHandle))
		{
			LocalWorld->GetTimerManager().SetTimer(ReleaseTimerHandle, this, &ThisClass::ReleaseNextComponents, ComponentLifespan); // 设置计时器
		}
	}

	// Determine the position
	// 确定位置
	FTransform CameraTransform; // 相机变换
	FVector NumberLocation(NewRequest.WorldLocation); // 数字位置
	if (APlayerController* PC = GetController<APlayerController>())
	{
		if (APlayerCameraManager* PlayerCameraManager = PC->PlayerCameraManager)
		{
			CameraTransform = FTransform(PlayerCameraManager->GetCameraRotation(), PlayerCameraManager->GetCameraLocation()); // 获取相机变换

			FVector LocationOffset(ForceInitToZero); // 位置偏移

			const float RandomMagnitude = 5.0f; //@TODO: Make this style driven // @TODO: 使其由样式驱动
			LocationOffset += FMath::RandPointInBox(FBox(FVector(-RandomMagnitude), FVector(RandomMagnitude))); // 随机位置偏移

			NumberLocation += LocationOffset; // 应用位置偏移
		}
	}
	PreparedNumberInfo.StaticMeshComponent->SetWorldTransform(FTransform(CameraTransform.GetRotation(), NumberLocation)); // 设置世界变换

	// Now apply the material parameters to make the digits, etc...
	// 现在应用材质参数以制作数字等...
	SetMaterialParameters(NewRequest, PreparedNumberInfo, CameraTransform, NumberLocation); // 设置材质参数
}

/**
 * 释放下一个组件
 */
void ULyraNumberPopComponent_MeshText::ReleaseNextComponents()
{
	UWorld* LocalWorld = GetWorld();
	check(LocalWorld);

	const float CurrentTime = LocalWorld->GetTimeSeconds(); // 当前时间

	int32 NumReleased = 0; // 释放数量
	for (const FLiveNumberPopEntry& LiveComp : LiveComponents)
	{
		if (CurrentTime >= LiveComp.ReleaseTime)
		{
			NumReleased++;
			if (ensure(LiveComp.Component))
			{
				LiveComp.Component->UnregisterComponent(); // 取消注册组件

				if (ensure(LiveComp.Pool))
				{
					// Return this component to the pool
					// 将此组件返回到池
					LiveComp.Pool->Components.Push(LiveComp.Component); // 推入组件池
				}
				else
				{
					// No pool. Just remove it.
					// 没有池。只需移除它。
					LiveComp.Component->SetFlags(RF_Transient); // 设置标志为瞬态
					LiveComp.Component->Rename(nullptr, GetTransientPackage(), RF_NoFlags); // 重命名到瞬态包
				}
			}
		}
		else
		{
			// These are in chronological order so none of the other elements will be deleted
			// 这些是按时间顺序排列的，因此其他元素都不会被删除
			break;
		}
	}

	// Actually remove it from the live components array
	// 实际上从活跃组件数组中移除它
	LiveComponents.RemoveAt(0, NumReleased); // 移除指定数量的元素

	// If we still have live components animating, set the timer to remove the next one
	// 如果我们还有活跃组件在动画中，设置计时器以移除下一个
	if (LiveComponents.Num() > 0)
	{
		const float TimeUntilNextRelease = LiveComponents[0].ReleaseTime - CurrentTime; // 计算直到下次释放的时间
		LocalWorld->GetTimerManager().SetTimer(ReleaseTimerHandle, this, &ThisClass::ReleaseNextComponents, TimeUntilNextRelease); // 设置计时器
	}
}

/**
 * 确定颜色
 */
FLinearColor ULyraNumberPopComponent_MeshText::DetermineColor(const FLyraNumberPopRequest& Request) const
{
	for (ULyraDamagePopStyle* Style : Styles)
	{
		if ((Style != nullptr) && Style->bOverrideColor)
		{
			if (Style->MatchPattern.Matches(Request.TargetTags))
			{
				return Request.bIsCriticalDamage ? Style->CriticalColor : Style->Color; // 如果是暴击则返回暴击颜色，否则返回普通颜色
			}
		}
	}

	return FLinearColor::White; // 默认返回白色
}

/**
 * 确定静态网格
 */
UStaticMesh* ULyraNumberPopComponent_MeshText::DetermineStaticMesh(const FLyraNumberPopRequest& Request) const
{
	for (ULyraDamagePopStyle* Style : Styles)
	{
		if ((Style != nullptr) && Style->bOverrideMesh)
		{
			if (Style->MatchPattern.Matches(Request.TargetTags))
			{
				return Style->TextMesh; // 返回文本网格
			}
		}
	}

	return nullptr; // 默认返回nullptr
}

/**
 * 设置材质参数
 */
void ULyraNumberPopComponent_MeshText::SetMaterialParameters(const FLyraNumberPopRequest& Request, FTempNumberPopInfo& NewDamageNumberInfo, const FTransform& CameraTransform, const FVector& NumberLocation)
{
	UWorld* World = GetWorld();
	if (World && GEngine)
	{
		const float RealGameTime = World->GetRealTimeSeconds(); // 真实游戏时间

		// Whether we should show a sign as the first digit, and if so which one
		// (if bIsSignNegative is true, we show minus, false is plus)
		// 我们是否应该将符号显示为第一个数字，如果是，是哪一个（如果 bIsSignNegative 为 true，我们显示减号，false 为加号）
		const bool bShouldShowSign = false; // 是否显示符号
		const bool bIsSignNegative = true; // 符号是否为负

		for (UMaterialInstanceDynamic* MeshMID : NewDamageNumberInfo.MeshMIDs)
		{
			MeshMID->SetScalarParameterValue(SignDigitParameterName, bIsSignNegative ? 0.5f : 0.0f); // 设置符号数字参数值
			MeshMID->SetVectorParameterValue(ColorParameterName, DetermineColor(Request)); // 设置颜色参数值

			// IF the damage number has more digits than we support
			// THEN force the damage number to the highest number we can support
			// 如果伤害数字的位数超过我们支持的位数，则将伤害数字强制设置为我们可以支持的最大数字
			const int32 MaxSupportedDigits = FMath::Min(FMath::Min(PositionParameterNames.Num(), ScaleRotationAngleParameterNames.Num()), DurationParameterNames.Num()); // 计算最大支持位数
			if (!ensure(NewDamageNumberInfo.DamageNumberArray.Num() <= MaxSupportedDigits))
			{
				NewDamageNumberInfo.DamageNumberArray.SetNum(MaxSupportedDigits); // 设置伤害数字数组大小为最大支持位数

				// Set all number digits to 9 so we show the largest number we can
				// Skip digit 0 because that digit is for the +/- sign
				// 将所有数字位设置为9，以便显示我们可以显示的最大数字。跳过数字0，因为该数字用于 +/- 符号。
				for (int32 DigitIndex = 1; DigitIndex < NewDamageNumberInfo.DamageNumberArray.Num(); ++DigitIndex)
				{
					NewDamageNumberInfo.DamageNumberArray[DigitIndex] = 9; // 设置为9
				}
			}

			MeshMID->SetScalarParameterValue(AnimationLifespanParameterName, ComponentLifespan); // 设置动画生命周期参数值
			MeshMID->SetScalarParameterValue(IsCriticalHitParameterName, Request.bIsCriticalDamage ? 1.f : 0.f); // 设置是否为暴击参数值

			const int32 DamageNumberArrayLength = NewDamageNumberInfo.DamageNumberArray.Num(); // 伤害数字数组长度
			float OffsetAccumulatedValue = (DamageNumberArrayLength * -1.f) + (bShouldShowSign ? 0.f : -1.f); // 偏移累计值

			const int32 LastIndex = (DamageNumberArrayLength >= 4) ? DamageNumberArrayLength : 4; // 最后索引

			for (int32 NumberIndex = 0; NumberIndex < LastIndex; ++NumberIndex)
			{
				const float NumberYOffset = ((NumberIndex / FMath::Max(1, DamageNumberArrayLength - 1)) - 0.5f) * 2.f; // 数字 Y 偏移
				const FVector NumberOffset = FVector(0.f, NumberYOffset, 0.f); // 数字偏移向量
				const FVector CameraSpaceDirection = CameraTransform.TransformVectorNoScale(NumberOffset); // 相机空间方向

				const float SpacingForNumber = ((NumberIndex < DamageNumberArrayLength) && ((NewDamageNumberInfo.DamageNumberArray[NumberIndex] == 1) || ((NumberIndex > 0) && (NewDamageNumberInfo.DamageNumberArray[NumberIndex - 1] == 1)))) ? SpacingPercentageForOnes : 1.f; // 数字间距
				OffsetAccumulatedValue += SpacingForNumber; // 累加偏移值

				FLinearColor RGBAPositionParameter(CameraSpaceDirection); // RGBAPositionParameter 向量
				RGBAPositionParameter.A = OffsetAccumulatedValue; // 设置 Alpha 为偏移累计值

				const FName PositionParameterName = PositionParameterNames[NumberIndex]; // 位置参数名称
				MeshMID->SetVectorParameterValue(PositionParameterName, RGBAPositionParameter); // 设置位置参数值

				const float DistanceFromCameraToNumber = (CameraTransform.GetLocation() - NumberLocation).Size(); // 从相机到数字的距离
				const float DistanceSpriteScale = DistanceFromCameraBeforeDoublingSize == 0.f ? 1.f : FMath::Clamp(DistanceFromCameraToNumber / DistanceFromCameraBeforeDoublingSize, 1.f, 1000000000.f); // 距离精灵缩放

				const float ScaleToZeroMultiplier = (NumberIndex < DamageNumberArrayLength) && (((NumberIndex == 0) && bShouldShowSign) || (NumberIndex != 0)) ? 1.f : 0.f; // 缩放到零乘数

				const float HitSizeMultiplier = Request.bIsCriticalDamage ? CriticalHitSizeMultiplier : 1.f; // 命中尺寸乘数
				const float FontSizeMultiplier = HitSizeMultiplier * DistanceSpriteScale * ScaleToZeroMultiplier; // 字体尺寸乘数

				FLinearColor RGBAScaleRotationParameter; // RGBAScaleRotationParameter 向量
				RGBAScaleRotationParameter.R = FontXSize * FontSizeMultiplier; // 设置 R 为字体 X 尺寸乘字体尺寸乘数
				RGBAScaleRotationParameter.G = FontYSize * FontSizeMultiplier; // 设置 G 为字体 Y 尺寸乘字体尺寸乘数
				RGBAScaleRotationParameter.B = NewDamageNumberInfo.DamageNumberArray[FMath::Min(DamageNumberArrayLength - 1, NumberIndex)]; // 设置 B 为伤害数字数组中的对应数字
				RGBAScaleRotationParameter.A = FMath::Sign(CameraSpaceDirection.X) * NumberOfNumberRotations; // 设置 A 为相机空间方向 X 的符号乘数字旋转次数

				const FName ScaleRotationAngleParameterName = ScaleRotationAngleParameterNames[NumberIndex]; // 缩放旋转角度参数名称
				MeshMID->SetVectorParameterValue(ScaleRotationAngleParameterName, RGBAScaleRotationParameter); // 设置缩放旋转角度参数值

				FLinearColor RGBADurationParameter; // RGBADurationParameter 向量
				RGBADurationParameter.R = RealGameTime + ComponentLifespan; // 设置 R 为真实游戏时间加组件生命周期
				RGBADurationParameter.G = FMath::FRand(); // 设置 G 为随机值

				const FName DurationParameterName = DurationParameterNames[NumberIndex]; // 持续时间参数名称
				MeshMID->SetVectorParameterValue(DurationParameterName, RGBADurationParameter); // 设置持续时间参数值
			}

			// Non-gameplay cameras while spectating have more cinematic values of aperture as default.
			// This makes damage numbers very blurry as they are brought close to the camera, and away from the point of focus.
			// Disable the shifting of numbers towards the camera here, if in a cinematic spectator camera.
			//@TODO: Determine whether or not we are spectating
			// 观察时的非游戏摄像机默认具有更电影化的光圈值。当伤害数字靠近相机并远离焦点时，这会使它们非常模糊。如果在电影观察者摄像机中，在此处禁用数字向相机的移动。@TODO: 确定我们是否正在观察。
			const bool bIsSpectating = false; // 是否正在观察
			MeshMID->SetScalarParameterValue(MoveToCameraParameterName, bIsSpectating ? 0.0f : 1.0f); // 设置移动到相机参数值
		}
	}
}